package edu.kit.ksri.db.mturk.plugins.performance;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;

/**
 * Copyright (c) 2010-2011 Karlsruhe Institute of Technology (KIT), Germany
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * 
 * --------------------------------------------------------------------------
 * 
 * Software: CSP/WMV tool for dynamic quality management of microtasks
 * http://www.ksri.kit.edu/Upload/Users/PeopleClouds/HCOMP2011_An_extendable_toolkit_for_managing_quality_of_human-based_eServices.pdf
 * 
 * Organization: Karlsruhe Institute of Technology (KIT), Germany
 * http://www.kit.edu
 * 
 * Project: PeopleClouds project of the Karlsruhe Service Research Institute(KSRI)
 * http://www.ksri.kit.edu/Default.aspx?PageId=818
 * 
 * Contributing authors: David Bermbach, Sandra Rath, Pascal Wichmann, Robert Kern
 */
public class LinearForecastWithHeavyDamping extends AbstractForecast {

	private static Logger basiclog = Logger
			.getLogger("basic." + LinearForecastWithHeavyDamping.class);
	
	private static final DecimalFormat df = new DecimalFormat("#,###,##0.0");

	@Override
	public void updateActivePoolSize(TimeConstraintsManager manager) {
		List<ProgressSnapshot> snaps = manager.getSnapshots();
		ArrayList<Double> assignmentsPerMinuteStats = new ArrayList<Double>();
		ProgressSnapshot last = null;
		synchronized (snaps) {
			last = snaps.get(snaps.size() - 1);
			for (ProgressSnapshot p : snaps) {
				assignmentsPerMinuteStats.add(p
						.getAssignmentsCompletedPerMinute());
			}
		}
		double remainingMinutes = getRemainingMinutes(last.getSnapshotDate(),
				manager.getDeadline());
		int activePoolSize = last.getActivePoolSize();
		double assignPerMin = getDampedInterpolation(assignmentsPerMinuteStats);
		int missingAssignments = (manager.getNumberOfAssignments())
				- last.getNumberOfAssignmentsCompleted();
		double perWorker = assignPerMin / activePoolSize;
		basiclog.info("Remaining minutes: " + df.format(remainingMinutes)
				+ "\n\tDamped Average: " + df.format(assignPerMin)
				+ " completed assignments/minute (i.e " + df.format(perWorker)
				+ " per worker)" + "\n\tMissing assignments: "
				+ missingAssignments);
		/*
		 * Calculation:
		 * 
		 * X people produce y units in t1 minutes, X*t1 people produce y units
		 * in 1 minute, X*t1 people produce y*t2 units in t2 minutes,
		 * (X*t1)/(y*t2) people produce 1 unit in t2 minutes, Z*(X*t1)/(y*t2)
		 * people produce Z units in t2 minutes.
		 * 
		 * X = activePoolSize; y = assignPerMin; t1 = 1 min; t2 =
		 * remainingMinutes; Z = missingAssignments; Z*(X*t1)/(y*t2) = new
		 * active pool size
		 */
		int newPoolSize = (int) Math.round(missingAssignments * activePoolSize
				* 1.0 / (assignPerMin * remainingMinutes));
		basiclog.info("\tNew active Pool size: " + newPoolSize);
		manager.updateActivePool(newPoolSize);

	}

	/**
	 * calculates the average of all values. It's damped in so far as current
	 * data gains a higher weight
	 * 
	 * @param assignmentsPerMinuteStats
	 * @return
	 */
	public double getDampedInterpolation(
			ArrayList<Double> assignmentsPerMinuteStats) {
		Double[] apm = assignmentsPerMinuteStats.toArray(new Double[1]);
		double res = 0;
		double totalweight = 0;
		for (int i = 0; i < apm.length; i++) {
			totalweight += 1.0 / (apm.length - i);
			res += apm[i] * 1.0 / (apm.length - i);
		}
		res = res / totalweight;
		return res;
	}

	/**
	 * 
	 * 
	 * @param start
	 * @param end
	 * 
	 * @return the difference between start and end in minutes.
	 * 
	 * 
	 */
	private double getRemainingMinutes(Date start, Date end) {
		long millis = end.getTime() - start.getTime();
		return millis / 60000.0;
	}

}
